package Database;

import Attributes.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;

public class DBLoader {

    /*
     * clase DBLoader
     *
     * Un objeto DBLoader se conecta a la base de datos y almacena los datos de
     * esta en estructuras internas.
     *
     * Llamar luego de su construccion a load() para realizar la carga.
     *
     * Llamar a getPersons() para obtener sus estructuras.
     *
     * IMPORTANTE: solo accede a bases de datos con el driver Derby
     *
     */
    private ArrayList<Person> personas;
    private String dbName;
    private String userName;
    private String password;
    private Connection con;

    public DBLoader(String dbName, String userName, String password) {
        this.dbName = dbName;
        this.userName = userName;
        this.password = password;
        personas = new ArrayList<Person>();
    }

    public ResultSet executeQuery(String query1) throws SQLException {
        PreparedStatement query = con.prepareStatement(query1, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
        ResultSet result = query.executeQuery();
        return result;
    }

    public void execute(String query1) throws SQLException {
        PreparedStatement query = con.prepareStatement(query1);
        query.execute();
    }

    public boolean tableExist(String tableName) {
        try {
            DatabaseMetaData dbmd = con.getMetaData();
            ResultSet rs = dbmd.getTables(null, null, tableName, null);
            if (!rs.next()) {
                return false;
            } else {
                return true;
            }
        } catch (Exception except) {
            except.printStackTrace();
            return false;
        }
    }

    public void connect() throws SQLException {
        con = ConnectionProvider.getInstance().getConnection("jdbc:derby:" + dbName + ";create=true", userName, password);
    }

    public void load() throws SQLException {
        /*
         * SUPONE LA EXISTENCIA DE UNA TABLA:
         *
         * APP.PERSONA (LU, NOMBRE [, campo_numerico1, campo_numerico2, ..])
         *
         */

        connect();
        // Si no existen las bases de datos, las crea
        createDatabases();
        ResultSet tablaPersona = executeQuery("SELECT * FROM APP.PERSONA");
        ResultSetMetaData tablaPersonaMD = tablaPersona.getMetaData();
        ResultSet tablaAtributo = executeQuery("SELECT * FROM APP.ATRIBUTO");
        /*
         * Volcado a estructuras internas
         */
        Hashtable<String, String> atributo_tipo = new Hashtable<String, String>();
        Hashtable<String, Double> atributo_umbral = new Hashtable<String, Double>();
        Hashtable<String, Double> atributo_factor = new Hashtable<String, Double>();

        while (tablaAtributo.next()) {
            String nomAtributo = tablaAtributo.getString("NOMBRE");
            atributo_factor.put(nomAtributo, tablaAtributo.getDouble("FACTOR"));
            atributo_tipo.put(nomAtributo, tablaAtributo.getString("TIPO"));
            if (tablaAtributo.getBoolean("EXCLUYENTE")) {
                if (atributo_tipo.get(nomAtributo).equals("CUALI")) //atributo_umbral.put(nomAtributo, Double.valueOf(Conversor.atributoAValor(nomAtributo, tablaAtributo.getString("UMBRAL"))));
                {
                    atributo_umbral.put(nomAtributo, Double.valueOf(tablaAtributo.getString("UMBRAL")));
                } else {
                    atributo_umbral.put(nomAtributo, tablaAtributo.getDouble("UMBRAL"));
                }
            }
        }
        int columnCount = tablaPersonaMD.getColumnCount();
        int p = 0;
        while (tablaPersona.next()) {
            p++;
            Person persona = new Person();
            for (int i = 1; i <= columnCount; i++) {
                String nomAtributo = tablaPersonaMD.getColumnName(i);
                //Crear un atributo del tipo que corresponda
                Attribute a = null;
                String tipo = atributo_tipo.get(nomAtributo);
                int flag = 0; // para diferenciar excluyentes
                if (tipo.equals("NAN")) {
                    a = new NANAttribute(nomAtributo, tablaPersona.getString(nomAtributo));
                } else if (tipo.equals("REAL")) {
                    flag = 1;
                    a = new SimpleAttribute(nomAtributo, tablaPersona.getDouble(nomAtributo), atributo_factor.get(nomAtributo));
                } else if (tipo.equals("INTEGER")) {
                    flag = 1;
                    a = new SimpleAttribute(nomAtributo, tablaPersona.getInt(nomAtributo), atributo_factor.get(nomAtributo));
                } else if (tipo.equals("CUALI")) {
                    a = new SimpleAttribute(nomAtributo, Conversor.atributoAValor(nomAtributo, tablaPersona.getString(nomAtributo)), atributo_factor.get(nomAtributo));
                } else {
                    System.out.println("Tipo de atributo no identificado: " + tipo);
                }
                // Se fija si el atributo que esta por agregar es excluyente
                if (atributo_umbral.get(nomAtributo) != null) {
                    a.setUmbral(atributo_umbral.get(nomAtributo), flag);
                }
                if (a != null) {
                    persona.addAttribute(a);
                }
            }
            personas.add(persona);
        }
        // Calcula el MAUT de cada persona
        for (Person per : personas) {
            per.calculateMAUT();
        }
        // Ordena a las personas por el MAUT
        Comparator comparator = Collections.reverseOrder();
        Collections.sort(personas, comparator);
    }

    public void createDatabases() throws SQLException {
        String query = "";
        if (!tableExist("PERSONA")) {
            System.out.print("Creando PERSONA... ");
            query = "CREATE TABLE APP.Persona ( "
                    // DATOS PERSONALES
                    + "NOMBRE VARCHAR(30) NOT NULL,"
                    + "APELLIDO VARCHAR(30) NOT NULL,"
                    + "DIA_NAC NUMERIC(2) NOT NULL," 
                    + "MES_NAC NUMERIC(2) NOT NULL," 
                    + "ANO_NAC NUMERIC(4) NOT NULL," 
                    + "ESTCIVIL VARCHAR(10),"
                    + "HIJOS VARCHAR(2),"
                    + "CANT_HIJOS NUMERIC(2),"
                    + "LU VARCHAR(6) PRIMARY KEY NOT NULL,"
                    + "TELEFONO VARCHAR(15),"
                    + "EMAIL VARCHAR(40) NOT NULL,"
                    + "DIRECCION VARCHAR(40),"
                    + "CIUDAD VARCHAR(40),"
                    + "PROVINCIA VARCHAR(40),"
                    // DATOS ACADEMICOS
                    + "CARRERA VARCHAR(40) NOT NULL,"
                    + "ANOINGR VARCHAR(4) NOT NULL,"
                    + "MATERIA1 VARCHAR(100),"
                    + "RECURSA1 VARCHAR(2),"
                    + "MATERIA2 VARCHAR(100),"
                    + "RECURSA2 VARCHAR(2),"
                    + "MATERIA3 VARCHAR(100),"
                    + "RECURSA3 VARCHAR(2),"
                    + "MATERIA4 VARCHAR(100),"
                    + "RECURSA4 VARCHAR(2),"
                    + "MATERIA5 VARCHAR(100),"
                    + "RECURSA5 VARCHAR(2),"
                    + "MATERIA6 VARCHAR(100),"
                    + "RECURSA6 VARCHAR(2),"
                    // DATOS GRUPO FAMILIAR
                    + "MONTO_INGR REAL NOT NULL,"
                    + "GRUPO_FAM NUMERIC(2) NOT NULL,"
                    + "HERMANOS VARCHAR(2) NOT NULL,"
                    + "HERMANOS_DEP NUMERIC(2),"
                    // SITUACION SOCIOECONÓMICA
                    + "FUENTE_INGR VARCHAR(40) NOT NULL,"
                    + "VIVE VARCHAR(30) NOT NULL,"
                    + "VIVE_OTRO VARCHAR(40),"
                    + "ALQUILA VARCHAR(2) NOT NULL,"
                    + "MONTO_ALQ REAL,"
                    + "GASTOS REAL NOT NULL,"
                    + "TRANSPORTE VARCHAR(15) NOT NULL)";
            execute(query);
            System.out.println("Creado");
        }
        if (!tableExist("ATRIBUTO")) {
            System.out.print("Creando ATRIBUTO... ");
            query = "CREATE TABLE APP.Atributo ( "
                    + "ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),"
                    + "NOMBRE VARCHAR(20),"
                    + "TIPO VARCHAR(10),"
                    + "FACTOR REAL,"
                    + "EXCLUYENTE BOOLEAN ,"
                    + "UMBRAL VARCHAR(40))";
            execute(query);
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('NOMBRE','NAN',0, FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('APELLIDO','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('DIA_NAC','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('MES_NAC','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('ANO_NAC','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('ESTCIVIL','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('HIJOS','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('CANT_HIJOS','INTEGER',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('LU','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('TELEFONO','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('EMAIL','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('DIRECCION','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('CIUDAD','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('PROVINCIA','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('CARRERA','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('ANOINGR','INTEGER',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('MATERIA1','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('RECURSA1','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('MATERIA2','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('RECURSA2','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('MATERIA3','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('RECURSA3','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('MATERIA4','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('RECURSA4','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('MATERIA5','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('RECURSA5','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('MATERIA6','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('RECURSA6','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('MONTO_INGR','REAL',0 , FALSE, '0')";;
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('GRUPO_FAM','INTEGER',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('HERMANOS','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('HERMANOS_DEP','INTEGER',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('FUENTE_INGR','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('VIVE','CUALI',0 , FALSE, '0')";;
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('VIVE_OTRO','NAN',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('ALQUILA','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('MONTO_ALQ','REAL',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('GASTOS','REAL',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            query = "INSERT INTO APP.Atributo(NOMBRE,TIPO,FACTOR,EXCLUYENTE,UMBRAL) VALUES ('TRANSPORTE','CUALI',0 , FALSE, '0')";
            execute(query);
            System.out.print(".");
            System.out.println("Creado");
        }
        if (!tableExist("COMENTARIO")) {
            System.out.print("Creando COMENTARIO... ");
            query = "CREATE TABLE APP.Comentario ( "
                    + "LU VARCHAR(6) PRIMARY KEY NOT NULL,"
                    + "COMENTARIO VARCHAR(500),"
                    + "FOREIGN KEY(LU) REFERENCES APP.Persona(LU))";
            execute(query);
            System.out.println("Creado");
        }
    }

    public ArrayList<Person> getPersons() {
        return personas;
    }

    public boolean isConnected() {
        return con != null;
    }
}
